package interfaces.subpanel.preprocesamiento.gui;

import interfaces.mainpanel.gui.TesisMainGUI;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;

import model.GenericScenarie;

import org.apache.commons.lang3.StringUtils;

import utils.ImagenesUtils;
import weka.core.Attribute;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.SparseInstance;
import weka.core.Utils;
import weka.core.stemmers.LovinsStemmer;
import weka.core.stemmers.NullStemmer;
import weka.core.stemmers.SnowballStemmer;
import weka.core.stemmers.Stemmer;
import weka.core.tokenizers.NGramTokenizer;
import weka.core.tokenizers.Tokenizer;
import weka.core.tokenizers.WordTokenizer;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.StringToWordVector;
import enumerations.AttributesEnum;

@SuppressWarnings({ "deprecation", "rawtypes", "unchecked" })
public class ARFFPanelGUI extends JPanel implements ActionListener {

	private static final String HEADER_DATA_SET = "Software quality requirements DataSet";

	private static final long serialVersionUID = 1L;

	private static final String DUMMY_CLASS = "DUMMY_CLASS";

	private static final String DUMMY_ID = "0";

	private static final String DUMMY_SCENARIE = "DUMMY_SCENARIE";

	private TesisMainGUI mainGUI;

	private JTextArea arffArea, arffProcessedArea;

	private Instances instances, testingInstances;

	private String arffFromExcelFile;

	private JComboBox stemmerList, tokenizerList;

	private String[] stemmerStrings = { " Porter Stemmer ",
			" English Stemmer ", " Lovins stemmer ", " Null Stemmer " };

	private String[] tokenizerStrings = { " Word Tokenizer ",
			" NGram Tokenizer " };

	private Stemmer stemmerSelected = new SnowballStemmer("porter");

	private Tokenizer tokenizerSelected = new WordTokenizer();

	private JButton processingButton;

	private FastVector attributes;

	private StringToWordVector filter;

	public ARFFPanelGUI(TesisMainGUI mainGUI) {

		super(new BorderLayout(5, 5));

		this.mainGUI = mainGUI;

		JPanel configurationRightPanel = new JPanel(new BorderLayout());
		this.processingButton = new JButton("Preprocessing",
				ImagenesUtils.createImageIcon(this.getClass(),
						"/processing.png"));
		this.processingButton.addActionListener(this);
		configurationRightPanel.add(this.processingButton, BorderLayout.CENTER);

		JPanel configurationLeftPanel = new JPanel(new GridLayout(2, 1, 20, 10));

		JPanel stemmerPanel = new JPanel(new BorderLayout(8, 2));
		this.stemmerList = new JComboBox(this.stemmerStrings);
		this.stemmerList.setSelectedIndex(0);
		this.stemmerList.addActionListener(this);
		JLabel stemmerLabel = new JLabel("Stemmer:", JLabel.LEFT);
		stemmerPanel.add(stemmerLabel, BorderLayout.WEST);
		stemmerPanel.add(this.stemmerList);

		JPanel tokenizerPanel = new JPanel(new BorderLayout(5, 2));
		this.tokenizerList = new JComboBox(this.tokenizerStrings);
		this.tokenizerList.setSelectedIndex(0);
		this.tokenizerList.addActionListener(this);
		JLabel tokenizerLabel = new JLabel("Tokenizer:", JLabel.LEFT);
		tokenizerPanel.add(tokenizerLabel, BorderLayout.WEST);
		tokenizerPanel.add(this.tokenizerList);

		configurationLeftPanel.add(stemmerPanel, 0);
		configurationLeftPanel.add(tokenizerPanel, 1);

		this.arffArea = new JTextArea();
		this.arffArea.setLineWrap(false);
		this.arffArea.setWrapStyleWord(false);
		this.arffArea.setEditable(false);
		this.arffArea.setColumns(0);
		this.arffArea.setMargin(new Insets(3, 3, 3, 3));

		this.arffProcessedArea = new JTextArea();
		this.arffProcessedArea.setLineWrap(false);
		this.arffProcessedArea.setWrapStyleWord(false);
		this.arffProcessedArea.setEditable(false);
		this.arffProcessedArea.setColumns(0);
		this.arffProcessedArea.setMargin(new Insets(3, 3, 3, 3));

		JTabbedPane arffTabbedPane = new JTabbedPane();
		JComponent arrFromExcelTab = new JScrollPane(this.arffArea);
		arffTabbedPane.addTab("From Excel", arrFromExcelTab);
		JComponent arrFromProcessed = new JScrollPane(this.arffProcessedArea);
		arffTabbedPane.addTab("Processed", arrFromProcessed);
		arffTabbedPane.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createCompoundBorder(
						BorderFactory.createTitledBorder("ARFF Panel: "),
						BorderFactory.createEmptyBorder(5, 5, 5, 5)),
				arffTabbedPane.getBorder()));

		JPanel configurationPanel = new JPanel(new BorderLayout(150, 1));
		configurationPanel.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createCompoundBorder(BorderFactory
						.createTitledBorder("Configuration Panel: "),
						BorderFactory.createEmptyBorder(5, 5, 5, 5)),
				configurationPanel.getBorder()));
		configurationPanel.add(configurationLeftPanel, BorderLayout.CENTER);
		configurationPanel.add(configurationRightPanel, BorderLayout.EAST);

		add(configurationPanel, BorderLayout.PAGE_START);
		add(arffTabbedPane, BorderLayout.CENTER);

		this.createAttributes();
		this.configureFilter();
	}

	public void actionPerformed(ActionEvent e) {

		if (e.getSource() == this.stemmerList) {

			switch (this.stemmerList.getSelectedIndex()) {
			case 0:
				this.stemmerSelected = new SnowballStemmer("porter");
				break;
			case 1:
				this.stemmerSelected = new SnowballStemmer("english");
				break;
			case 2:
				this.stemmerSelected = new LovinsStemmer();
				break;
			case 3:
				this.stemmerSelected = new NullStemmer();
				break;
			}

		} else if (e.getSource() == this.tokenizerList) {

			switch (this.tokenizerList.getSelectedIndex()) {
			case 0:
				this.tokenizerSelected = new WordTokenizer();
				break;
			case 1:
				this.tokenizerSelected = new NGramTokenizer();
				break;
			}

		} else if (e.getSource() == this.processingButton) {
			this.configureFilter();
			if ((this.mainGUI.getLoadDataSetGUI().isManualSelectionSplitRb() || this.mainGUI
					.getLoadDataSetGUI().isPercentageSelectionSplitRb())
					&& this.getMainGUI().getLoadDataSetGUI()
							.getTestingDataTable().dataSize() == 0) {
				JOptionPane
						.showMessageDialog(
								this,
								"Couldn't run ARFF button because the testing table is empty.",
								"Empty Testing DataSet!",
								JOptionPane.ERROR_MESSAGE, ImagenesUtils
										.createImageIcon(this.getClass(),
												"/stop.png"));
				return;
			}

			if (this.mainGUI.getLoadDataSetGUI().getTrainingDataTable()
					.isNotEmpty()) {
				this.retrieveDataSet();
				if (this.mainGUI.getLoadDataSetGUI().isManualSelectionSplitRb()
						|| this.mainGUI.getLoadDataSetGUI()
								.isPercentageSelectionSplitRb())
					retrieveDataSetForTesting();
				filteringDataWithStringToWordVector();
				this.arffArea.setCaretPosition(0);
				this.arffProcessedArea.setCaretPosition(0);
			} else
				JOptionPane.showMessageDialog(this,
						"Couldn't run ARFF button because the table is empty.",
						"Empty DataSet!", JOptionPane.ERROR_MESSAGE,
						ImagenesUtils.createImageIcon(this.getClass(),
								"/stop.png"));
		}
	}

	public Instances retrieveDataSet() {

		if (this.getMainGUI().getLoadDataSetGUI().getTrainingDataTable()
				.dataSize() > 0) {

			this.instances = new Instances(HEADER_DATA_SET, this.attributes,
					10000);

			// Add a dummy instance for the SparceIntance class bug.
			Instance dummyInstance = this.createDummyInstance();
			dummyInstance.setDataset(this.instances);
			this.instances.add(dummyInstance);

			for (int i = 0; i < this.getMainGUI().getLoadDataSetGUI()
					.getTrainingDataTable().dataSize(); i++) {
				Instance instance = this.createInstace(this.getMainGUI()
						.getLoadDataSetGUI().getTrainingDataTable()
						.getGenericScenarie(i));
				instance.setDataset(this.instances);
				this.instances.add(instance);
			}

			// Remove element at 0 because it's the dummy scenarie.
			this.instances.remove(0);
			this.arffFromExcelFile = this.instances.toString();
			this.arffFromExcelFile = this.arffFromExcelFile.replaceFirst(
					DUMMY_CLASS + ",", "");
			this.arffArea.setText(this.arffFromExcelFile);
		}

		return this.instances;
	}

	/**
	 * We only use this method when Test Options is selected in 'Manual
	 * Selection Split'.
	 * 
	 * @return Instances set of instances for testing.
	 */
	public Instances retrieveDataSetForTesting() {

		if (this.getMainGUI().getLoadDataSetGUI().getTestingDataTable()
				.dataSize() > 0) {

			this.testingInstances = new Instances(HEADER_DATA_SET,
					this.attributes, 10000);

			// Add a dummy instance for the SparceIntance class bug.
			Instance dummyInstance = this.createDummyInstance();
			dummyInstance.setDataset(this.testingInstances);
			this.testingInstances.add(dummyInstance);

			for (int i = 0; i < this.getMainGUI().getLoadDataSetGUI()
					.getTestingDataTable().dataSize(); i++) {
				Instance instance = this.createInstace(this.getMainGUI()
						.getLoadDataSetGUI().getTestingDataTable()
						.getGenericScenarie(i));
				instance.setDataset(this.testingInstances);
				this.testingInstances.add(instance);
			}
			// Remove element at 0 because it's the dummy scenarie.
			this.testingInstances.remove(0);
		}

		return testingInstances;
	}

	private void createAttributes() {
		this.attributes = new FastVector(3);
		this.attributes
				.addElement(new Attribute("scenarie", (FastVector) null));
		FastVector classValues = new FastVector(
				AttributesEnum.values().length + 1);
		classValues.addElement(DUMMY_CLASS);
		for (int cantAttributes = 0; cantAttributes < AttributesEnum.values().length; cantAttributes++) {
			classValues.addElement(AttributesEnum.values()[cantAttributes]
					.toString());
		}
		this.attributes.addElement(new Attribute("@class@", classValues));
		this.attributes.addElement(new Attribute("scenarieID",
				Attribute.NUMERIC));
	}

	private Instance createInstace(GenericScenarie scenarie) {
		Instance instance = new SparseInstance(3);
		String quotedAttribute = Utils.quote(scenarie.getScenarie());
		String unQuotedAttribute = removeFirstAndLastQuote(quotedAttribute);
		instance.setValue((Attribute) this.attributes.elementAt(0),
				unQuotedAttribute);
		instance.setValue((Attribute) this.attributes.elementAt(1), scenarie
				.getAttribute().toString());
		instance.setValue((Attribute) this.attributes.elementAt(2),
				Integer.valueOf(scenarie.getId()).intValue());

		return instance;
	}

	private Instance createDummyInstance() {
		Instance dummyInstance = new SparseInstance(3);
		dummyInstance.setValue((Attribute) this.attributes.elementAt(0),
				DUMMY_SCENARIE);
		dummyInstance.setValue((Attribute) this.attributes.elementAt(1),
				DUMMY_CLASS);
		dummyInstance.setValue((Attribute) this.attributes.elementAt(2),
				Integer.valueOf(DUMMY_ID).intValue());

		return dummyInstance;
	}

	private void filteringDataWithStringToWordVector() {

		try {
			this.filter.setInputFormat(this.instances);
			Instances filteredTrainInstances = Filter.useFilter(this.instances,
					this.filter);
			filteredTrainInstances.setClassIndex(0);
			Instances filteredTestInstances = null;
			if (this.mainGUI.getLoadDataSetGUI().isManualSelectionSplitRb()
					|| this.mainGUI.getLoadDataSetGUI()
							.isPercentageSelectionSplitRb()) {
				filteredTestInstances = Filter.useFilter(this.testingInstances,
						this.filter);
				filteredTestInstances.setClassIndex(0);
			}
			String arffProcessed = filteredTrainInstances.toString()
					.replaceFirst(DUMMY_CLASS + ",", "");
			this.arffProcessedArea.setText(arffProcessed);
			this.mainGUI.getClassifierGUI().setFilteredInstancesForTraining(
					filteredTrainInstances);
			if (this.mainGUI.getLoadDataSetGUI().isManualSelectionSplitRb()
					|| this.mainGUI.getLoadDataSetGUI()
							.isPercentageSelectionSplitRb()) {
				this.mainGUI.getClassifierGUI().setFilteredInstancesForTesting(
						filteredTestInstances);
			}
		} catch (Exception e) {
			JOptionPane
					.showMessageDialog(this, e.getMessage(), "ERROR!",
							JOptionPane.ERROR_MESSAGE, ImagenesUtils
									.createImageIcon(this.getClass(),
											"/stop.png"));
		}
	}

	private String removeFirstAndLastQuote(String quotedAttribute) {

		String unQuotedAttribute = quotedAttribute;
		if (StringUtils.startsWith(quotedAttribute, "'"))
			unQuotedAttribute = quotedAttribute.substring(1);
		if (StringUtils.endsWith(quotedAttribute, "'"))
			unQuotedAttribute = unQuotedAttribute.substring(0,
					unQuotedAttribute.length() - 1);

		return unQuotedAttribute;
	}

	private void configureFilter() {
		this.filter = new StringToWordVector();
		this.filter.setUseStoplist(true);
		this.filter.setLowerCaseTokens(true);
		this.filter.setStopwords(new File(getClass().getResource(
				"/englishStopWords.txt").getFile()));
		this.filter.setIDFTransform(true);
		this.filter.setTFTransform(true);
		this.filter.setStemmer(this.stemmerSelected);
		this.filter.setTokenizer(this.tokenizerSelected);
	}

	public TesisMainGUI getMainGUI() {
		return mainGUI;
	}

	public String getStemmerName() {
		return this.stemmerList.getSelectedItem().toString();
	}

	public String getTokenizerName() {
		return this.tokenizerList.getSelectedItem().toString();
	}

	public Stemmer getStemmerSelected() {
		return stemmerSelected;
	}

}